package com.electronic.modules.electronicSealSignature;

import cn.hutool.core.io.FileUtil;
import com.alibaba.fastjson.JSON;
import com.cebpubservice.cashare.sdk.electronicsealsignatureandverifysignature.ElectronicSealSignature;
import com.cebpubservice.cashare.sdk.electronicsealsignatureandverifysignature.param.ExitElectronicSealSignatureParamDTO;
import com.cebpubservice.cashare.sdk.electronicsealsignatureandverifysignature.param.GetElectronicSealSignatureUserInfoParamDTO;
import com.cebpubservice.cashare.sdk.electronicsealsignatureandverifysignature.param.GetUserSealInfoParamDTO;
import com.cebpubservice.cashare.sdk.electronicsealsignatureandverifysignature.result.GetElectronicSealSignatureUserInfoResultDTO;
import com.cebpubservice.cashare.sdk.electronicsealsignatureandverifysignature.result.GetUserSealInfoResultDTO;
import com.electronic.constant.CommonConstant;
import com.electronic.domain.*;
import com.electronic.enums.*;
import com.electronic.exception.ResultCode;
import com.electronic.exception.ServiceException;
import com.electronic.gbt.GBT6_1_1.SESeal;
import com.electronic.gbt.GBT6_1_2_4.SES_ESPropertyInfo;
import com.electronic.gbt.GBT6_1_2_5.SES_ESPictrueInfo;
import com.electronic.modules.electronicSealSignature.param.ExitElectronicSealSignatureDTO;
import com.electronic.modules.electronicSealSignature.param.StartSignDTO;
import com.electronic.utils.ICertUtil;
import com.electronic.utils.IFCAUtil;
import com.electronic.utils.pdf.PDFTools;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Base64Utils;
import org.springframework.util.StopWatch;
import org.springframework.util.StringUtils;

import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

@Slf4j
public class ExecuteSignatureService {

    public String executeSignature(StartSignDTO startSignDTO) {
        // 获取扫码用户信息
        GetElectronicSealSignatureUserInfoParamDTO userInfoParamDTO = new GetElectronicSealSignatureUserInfoParamDTO();
        userInfoParamDTO.setTid(startSignDTO.getTid());
        GetElectronicSealSignatureUserInfoResultDTO userInfoResultDTO = ElectronicSealSignature.getElectronicSealSignatureUserInfo(userInfoParamDTO);

        StopWatch stopWatch = new StopWatch("签章前置处理");
        stopWatch.start("查询签章信息");

        stopWatch.stop();
        stopWatch.start("获取签章者证书");
        //签章者证书
        String signCertStr = userInfoResultDTO.getSignatureCert();
        X509Certificate signX509Certificate = null;
        try {
            //将签章者证书字符串转证书对象
            signX509Certificate = ICertUtil.generateCertificate(Base64Utils.decodeFromString(signCertStr));
        } catch (CertificateException e) {
            log.error("解析签章者：签名证书：{}异常", userInfoResultDTO.getSignatureCertSn());
            return CommonConstant.STATUS_30;
        }
        stopWatch.stop();
        stopWatch.start("调用中招SDK获取印章");
        //调用中招SDK获取印章
        GetUserSealInfoParamDTO paramDTO = new GetUserSealInfoParamDTO();
        paramDTO.setTid(startSignDTO.getTid());
        paramDTO.setSealSn(startSignDTO.getSealSn());
        paramDTO.setSignatureCertSn(userInfoResultDTO.getSignatureCertSn());
        paramDTO.setAccessToken(userInfoResultDTO.getAccessToken());
        paramDTO.setCaOrgCode(userInfoResultDTO.getCaOrgCode());
        //SDK签章者印章返回对象
        GetUserSealInfoResultDTO sealInfoResultDTO = ElectronicSealSignature.getUserSealInfo(paramDTO);
        //签章者印章
        SESeal seSeal = null;
        if (Objects.isNull(sealInfoResultDTO)) {
            log.error("调用接口获取签章者：{}，SDK返回null", JSON.toJSONString(paramDTO));
            log.error("用户已退出签章，无法获取签名值，请重新扫码", JSON.toJSONString(paramDTO));
            return CommonConstant.STATUS_30;
        } else {
            seSeal = SESeal.getInstance(Base64Utils.decodeFromString(sealInfoResultDTO.getSealInfo()));
        }
        //印章图片
        SES_ESPictrueInfo pictrueInfo = seSeal.geteSealInfo().getPicture();
        //印章属性
        SES_ESPropertyInfo propertyInfo = seSeal.geteSealInfo().getProperty();

        //印章大小
        float sealWidth = pictrueInfo.getWidth().getValue().floatValue();
        float sealHeight = pictrueInfo.getHeight().getValue().floatValue();
//        SealSize sealImgSize = IFCAUtil.calculateSealSize(pictureInfo.getData().getOctets(), startSignDTO.getSealImgMaxValue());
        SealSize sealImgSize = null;
        //获取印章图片类型
        SealTypeEnum sealTypeEnum = SealTypeEnum.getByType("0"+propertyInfo.getType().toString());
        if (Objects.isNull(sealTypeEnum)) {
            log.info("签章时未获取到印章图片类型{}枚举对象，使用默认图片大小", propertyInfo.getType().toString());
            sealImgSize = IFCAUtil.calculateSealSize(pictrueInfo.getData().getOctets(), 113, 113);
        } else {
            sealImgSize = IFCAUtil.calculateSealSize(pictrueInfo.getData().getOctets(), sealTypeEnum.getWidthPx(), sealTypeEnum.getHeightPx());
        }
        sealWidth = sealImgSize.getWidth();
        sealHeight = sealImgSize.getHeight();


        stopWatch.stop();
        stopWatch.start("获取待签文件");
        //查询待签文件
        String fileAbsolutePath = startSignDTO.getLocalFilePath();
        if (!FileUtil.exist(fileAbsolutePath)) {
            log.error("，未获取待签署文件：{}", startSignDTO.getLocalFilePath());
            return CommonConstant.STATUS_30;
        }
        stopWatch.stop();
        //骑缝章签署
        if (SignatureTypeEnum.PAGESEAL.getType().equals(startSignDTO.getSignatureInfo().getSignatureType())) {
            //构建骑缝章坐标对象
            PositionInfo positionInfo = new PositionInfo();
            positionInfo.setCrossPage(true);
            positionInfo.setPositionX(startSignDTO.getSignatureInfo().getX());
            positionInfo.setPositionY(startSignDTO.getSignatureInfo().getY());
            positionInfo.setImgWidth(sealWidth);
            positionInfo.setImgHeight(sealHeight);
            stopWatch.start("骑缝章签署");
            //开始骑缝章签署
            doSignature(startSignDTO, Arrays.asList(positionInfo), seSeal, userInfoResultDTO.getAccessToken(), signX509Certificate, PositionTypeEnum.COORD);
            stopWatch.stop();
            log.info(stopWatch.prettyPrint());
            log.info("执行骑缝签章总耗时:{}", stopWatch.getTotalTimeSeconds());
            return CommonConstant.STATUS_20;
        }

        //普通章签署
        //实际需要签署的位置
        stopWatch.start("计算签章真实位置");
        List<PositionInfo> positionInfoList = null;
        PositionTypeEnum positionTypeEnum = null;
        //PDF文件
        if (FileTypeEnum.PDF.getType().equals(startSignDTO.getAwaitingSignatureFileType())) {
            if (StringUtils.hasText(startSignDTO.getSignatureKeyWord())) {
                positionTypeEnum = PositionTypeEnum.KEYWORD;
                //通过关键字计算签章实际签署位置
                positionInfoList = PDFTools.keywordReturnPositionInfo(fileAbsolutePath, startSignDTO.getSignatureInfo().getPage(), startSignDTO.getSignatureKeyWord(), sealWidth, sealHeight);
            } else {
                positionTypeEnum = PositionTypeEnum.COORD;
                //构造坐标签署位置
                positionInfoList = PDFTools.coordinateReturnPositionInfo(fileAbsolutePath, startSignDTO.getSignatureInfo().getPage(), startSignDTO.getSignatureInfo().getX(), startSignDTO.getSignatureInfo().getY(), sealWidth, sealHeight);
            }
        } else {
            //OFD逻辑
        }

        stopWatch.stop();
        log.info("签章前置处理总耗时:{}", stopWatch.getTotalTimeSeconds());
        stopWatch.start("执行签章");
        //开始执行签署
        doSignature(startSignDTO, positionInfoList, seSeal, userInfoResultDTO.getAccessToken(), signX509Certificate, positionTypeEnum);
        stopWatch.stop();
        log.info(stopWatch.prettyPrint());
        log.info("执行签章总耗时:{}", stopWatch.getTotalTimeSeconds());
        return CommonConstant.STATUS_20;
    }

    /**
     * @param startSignDTO        签章文件对象
     * @param positionInfoList    位置集合
     * @param seSeal              印章结构体对象
     * @param accessToken         中招token
     * @param signX509Certificate 签名证书
     * @return void
     * @description 开始签章
     */
    private void doSignature(
            StartSignDTO startSignDTO,
            List<PositionInfo> positionInfoList,
            SESeal seSeal,
            String accessToken,
            X509Certificate signX509Certificate,
            PositionTypeEnum positionTypeEnum) {
        // 换成前台传递的绝对路径
        String fileAbsolutePath = startSignDTO.getLocalFilePath();
        try {
            //PDF文件签署
            if (FileTypeEnum.PDF.getType().equalsIgnoreCase(startSignDTO.getAwaitingSignatureFileType())) {
                //PDF文件坐标签署
                new PdfSignatureGBTService(new CebsSdkProperties(), new SignProperties()).signature(startSignDTO, fileAbsolutePath, seSeal, accessToken, signX509Certificate, positionInfoList, "", "", positionTypeEnum);
            } else {
                //OFD文件签署
            }

        } catch (Exception e) {
            log.error("，文件：签署异常", e);
            throw new ServiceException(ResultCode.ERROR_SIGNATURE, ResultCode.ERROR_SIGNATURE.getMessage());
        }
        //调用中招关闭签章
        //是否连续签章 00:单次签章 、01：连续签章 -- 后续优化为读取前端选择的签章行为
//        if (SignContinuedEnum.SIGN_01.getType().equalsIgnoreCase(startSignDTO.getSignContinued())) {
//            exitSealSignature(startSignDTO.getTid(), startSignDTO.getPid(), accessToken);
//        }


    }

    /**
     * @return void
     * @description 处理签章结果
     */
    private void processingSignatureResults(
            String finalFile) {
        StopWatch stopWatch = new StopWatch("签章完成处理");
        stopWatch.stop();
        log.info(stopWatch.prettyPrint());
        log.info("签章完成处理总耗时:{}", stopWatch.getTotalTimeSeconds());
    }

    /**
     * 关闭连续签章
     *
     * @param tid
     * @param pid
     * @param accessToken
     * @param source
     */
    private static boolean exitSealSignature(String tid, String pid, String accessToken, ExitSignatureSourceEnum source) {
        try {
            ExitElectronicSealSignatureParamDTO exitParamDTO = new ExitElectronicSealSignatureParamDTO();
            exitParamDTO.setTid(tid);
            exitParamDTO.setPid(pid);
            exitParamDTO.setAccessToken(accessToken);
            exitParamDTO.setSource(source.getSource());
            boolean exitSuccess = ElectronicSealSignature.exitElectronicSealSignature(exitParamDTO);
            if (!exitSuccess) {
                throw new ServiceException(ResultCode.ERROR_CLOSE_SIGNATURE, ResultCode.ERROR_CLOSE_SIGNATURE.getMessage());
            }
            return exitSuccess;
        } catch (Exception e) {

            throw new ServiceException(ResultCode.ERROR_CLOSE_SIGNATURE, ResultCode.ERROR_CLOSE_SIGNATURE.getMessage());
        }

    }


    /**
     * 退出连续签章
     *
     * @return
     */
    public static boolean exitElectronicSealSignature(ExitElectronicSealSignatureDTO exitElectronicSealSignature) {
//        String reqTid = IRequestUtil.getReqTransactionId();
        // 初始化Ca
        CebsConfiguration.init(exitElectronicSealSignature.getCebsSdkProperties());
        //判断是否已经生成是否为空
        if (Objects.isNull(exitElectronicSealSignature.getTid())) {
            throw new ServiceException(ResultCode.ERROR_SIGNATURE_FAIL_USER_NOT_SCAN, ResultCode.ERROR_SIGNATURE_FAIL_USER_NOT_SCAN.getMessage());
        }
        //判断二维码状态
        if (!exitElectronicSealSignature.getQrCodeScannedStatus().equals(QRCodeScannedStatusEnum.SIGNED.getCode())) {
            throw new ServiceException(ResultCode.ERROR_SIGNATURE_FAIL_USER_NOT_SCAN, ResultCode.ERROR_SIGNATURE_FAIL_USER_NOT_SCAN.getMessage());
        }

        // 获取扫码用户信息
        GetElectronicSealSignatureUserInfoParamDTO userInfoParamDTO = new GetElectronicSealSignatureUserInfoParamDTO();
        userInfoParamDTO.setTid(exitElectronicSealSignature.getPid());
        GetElectronicSealSignatureUserInfoResultDTO userInfoResultDTO = ElectronicSealSignature.getElectronicSealSignatureUserInfo(userInfoParamDTO);
        // 判断用户信息是否获取成功
        if (userInfoResultDTO == null) {
//            log.error("未获取到userInfoResultDTO，无法执行退出签章");
//            log.error(JSON.toJSONString(R.fail(HttpServletResponse.SC_BAD_REQUEST,ResultCode.ERROR_USER_NOT.getErrorCode(), ResultCode.ERROR_USER_NOT.getErrorCode())));
            throw new ServiceException(ResultCode.ERROR_USER_NOT, ResultCode.ERROR_USER_NOT.getErrorCode());

        }
//        //从redis中获取Pid(签章流程唯一标识)
        return exitSealSignature(exitElectronicSealSignature.getTid(), exitElectronicSealSignature.getPid(), userInfoResultDTO.getAccessToken(), ExitSignatureSourceEnum.USER);
    }

}
